#ifndef S21_CONTAINER_MAP_H
#define S21_CONTAINER_MAP_H

#include <initializer_list>
#include <stdexcept>
#include <utility>
#include <vector>

#include "../RBTree/tree.h"

namespace s21 {

template <typename Key, typename T>
class map : public RBTree<Key, T> {
 public:
  class MapIterator;
  class ConstMapIterator;

  using key_type = Key;
  using mapped_type = T;
  using value_type = std::pair<const key_type, mapped_type>;
  using iterator = MapIterator;
  using const_iterator = ConstMapIterator;
  using reference = value_type&;
  using const_referece = const value_type&;
  using size_type = std::size_t;

  map() : RBTree<Key, T>(){};
  map(std::initializer_list<value_type> const& key_mapped_pairs);
  map(const map<key_type, mapped_type>& other) : RBTree<Key, T>(other){};
  map(map<key_type, mapped_type>&& other) : RBTree<Key, T>(std::move(other)){};
  ~map() = default;

  map<key_type, mapped_type> operator=(map<key_type, mapped_type>&& other);

  bool empty();
  size_type size();
  size_type max_size();
  mapped_type& at(const key_type key);
  mapped_type& operator[](const key_type key);
  iterator begin();
  const_iterator cbegin();
  iterator end();
  const_iterator cend();

  void clear();
  std::pair<iterator, bool> insert(const value_type& value);
  std::pair<iterator, bool> insert(const key_type& key, const mapped_type& obj);
  std::pair<iterator, bool> insert_or_assign(const key_type& key,
                                             const mapped_type& obj);
  template <class... Args>
  std::vector<std::pair<iterator, bool>> insert_many(Args&&... args);
  void erase(iterator pos);
  void swap(map<key_type, mapped_type>& other);
  void merge(map<key_type, mapped_type>& other);
  bool contains(const key_type& key);

  class MapIterator : public RBTree<Key, T>::RBTreeIterator {
    friend class map;

   public:
    MapIterator() : RBTree<Key, T>::RBTreeIterator(){};
    MapIterator(typename RBTree<Key, T>::Node_ptr start_node,
                typename RBTree<Key, T>::Node_ptr null_node)
        : RBTree<Key, T>::RBTreeIterator(start_node, null_node){};
    value_type& operator*();
  };

  class ConstMapIterator : public MapIterator {
    friend class map;

   public:
    ConstMapIterator(typename RBTree<Key, T>::Node_ptr start_node,
                     typename RBTree<Key, T>::Node_ptr null_node)
        : RBTree<Key, T>::ConstIterator(start_node, null_node){};
    value_type& operator*() const;
  };
};

template <typename key_type, typename mapped_type>
map<key_type, mapped_type>::map(
    std::initializer_list<value_type> const& key_mapped_pairs) {
  for (const value_type& item : key_mapped_pairs) {
    RBTree<key_type, mapped_type>::InsertNode(item.first, item.second);
  }
}

template <typename key_type, typename mapped_type>
map<key_type, mapped_type> map<key_type, mapped_type>::operator=(
    map<key_type, mapped_type>&& other) {
  if (this != &other) {
    RBTree<key_type, mapped_type>::operator=(std::move(other));
  }
  return *this;
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::mapped_type&
map<key_type, mapped_type>::at(const key_type key) {
  typename RBTree<key_type, mapped_type>::RBTreeIterator it =
      RBTree<key_type, mapped_type>::Find(key);
  if (it == RBTree<key_type, mapped_type>::end()) {
    throw std::out_of_range("there is no element with this key");
  }
  return it->second;
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::mapped_type&
map<key_type, mapped_type>::operator[](const key_type key) {
  // typename map<key_type, mapped_type>::mapped_type result = mapped_type{};
  auto it = RBTree<key_type, mapped_type>::Find(key);
  if (it == end()) {
    auto insrt = insert(std::make_pair(key, mapped_type{}));
    it = insrt.first;
  }
  return it->second;
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::iterator
map<key_type, mapped_type>::begin() {
  return MapIterator(RBTree<key_type, mapped_type>::Minimum(this->root),
                     this->final_leaf);
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::iterator
map<key_type, mapped_type>::end() {
  MapIterator result;
  // if (RBTree<key_type, mapped_type>::root == nullptr ||
  //     RBTree<key_type, mapped_type>::root ==
  //         RBTree<key_type, mapped_type>::final_leaf) {
  //   result = begin();
  // } else {
  typename RBTree<key_type, mapped_type>::Node_ptr last =
      RBTree<key_type, mapped_type>::MaxNode(this->root);
  MapIterator res(last, nullptr);
  result = res;
  // MapIterator res(nullptr, last);
  // }
  return result;
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::const_iterator
map<key_type, mapped_type>::cbegin() {
  return ConstMapIterator(RBTree<key_type, mapped_type>::Minimum(this->root),
                          this->final_leaf);
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::const_iterator
map<key_type, mapped_type>::cend() {
  typename RBTree<key_type, mapped_type>::Node_ptr last =
      RBTree<key_type, mapped_type>::MaxNode(this->root);
  ConstMapIterator res(nullptr, last);
  return res;
}

template <typename key_type, typename mapped_type>
bool map<key_type, mapped_type>::empty() {
  return RBTree<key_type, mapped_type>::Empty();
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::size_type
map<key_type, mapped_type>::size() {
  return RBTree<key_type, mapped_type>::Size();
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::size_type
map<key_type, mapped_type>::max_size() {
  return RBTree<key_type, mapped_type>::MaxSize();
}

template <typename key_type, typename mapped_type>
void map<key_type, mapped_type>::clear() {
  return RBTree<key_type, mapped_type>::Clear();
}

template <typename key_type, typename mapped_type>
std::pair<typename map<key_type, mapped_type>::iterator, bool>
map<key_type, mapped_type>::insert(const value_type& value) {
  return insert(value.first, value.second);
}

template <typename key_type, typename mapped_type>
std::pair<typename map<key_type, mapped_type>::iterator, bool>
map<key_type, mapped_type>::insert(const key_type& key,
                                   const mapped_type& obj) {
  std::pair<iterator, bool> res_val{};
  std::pair<typename RBTree<key_type, mapped_type>::RBTreeIterator, bool> tmp =
      RBTree<key_type, mapped_type>::Insert(key, obj);
  res_val.second = tmp.second;
  typename RBTree<key_type, mapped_type>::RBTreeIterator tree_it = tmp.first;
  typename RBTree<key_type, mapped_type>::Node_ptr res_node = tree_it.GetNode();
  iterator res_it(res_node, nullptr);
  res_val.first = res_it;
  return res_val;
}

template <typename key_type, typename mapped_type>
std::pair<typename map<key_type, mapped_type>::iterator, bool>
map<key_type, mapped_type>::insert_or_assign(const key_type& key,
                                             const mapped_type& obj) {
  auto it = RBTree<key_type, mapped_type>::Find(key);
  std::pair<typename map<key_type, mapped_type>::iterator, bool> result;
  bool result_bool = true;
  if (it != this->end()) {
    RBTree<key_type, mapped_type>::Erase(it);
    // result = insert(key, obj);
    result_bool = false;
    // res.second = false;
    // return res;
  }
  result = insert(key, obj);
  result.second = result_bool;
  return result;
}

template <typename key_type, typename mapped_type>
void map<key_type, mapped_type>::erase(iterator pos) {
  RBTree<key_type, mapped_type>::Erase(pos);
}

template <typename key_type, typename mapped_type>
void map<key_type, mapped_type>::swap(map<key_type, mapped_type>& pos) {
  RBTree<key_type, mapped_type>::Swap(pos);
}

template <typename key_type, typename mapped_type>
void map<key_type, mapped_type>::merge(map<key_type, mapped_type>& pos) {
  RBTree<key_type, mapped_type>::Merge(pos);
}

template <typename key_type, typename mapped_type>
bool map<key_type, mapped_type>::contains(const key_type& key) {
  return RBTree<key_type, mapped_type>::Contains(key);
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::value_type&
map<key_type, mapped_type>::MapIterator::operator*() {
  std::pair<const key_type, mapped_type>& temp_res =
      RBTree<key_type, mapped_type>::RBTreeIterator::current->data;
  // std::pair<const key_type, mapped_type>& res = temp_res;
  return temp_res;
}

template <typename key_type, typename mapped_type>
typename map<key_type, mapped_type>::value_type&
map<key_type, mapped_type>::ConstMapIterator::operator*() const {
  std::pair<const key_type, mapped_type>& temp_res =
      RBTree<key_type, mapped_type>::ConstIterator::current->data;
  // std::pair<const key_type, mapped_type>& res = temp_res;
  return temp_res;
}

template <typename key_type, typename mapped_type>
template <class... Args>
std::vector<std::pair<typename map<key_type, mapped_type>::iterator, bool>>
map<key_type, mapped_type>::insert_many(Args&&... args) {
  std::vector<std::pair<typename map<key_type, mapped_type>::iterator, bool>>
      res;
  for (const auto& a : {args...}) {
    res.push_back(insert(a));
  }
  return res;
}

}  // namespace s21
#endif  // S21_CONTAINER_MAP_H